// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ------------------------------------------------------------------
// Modification history:
// feature: Default value no longer case-sensitive.
// feature: Dynamic types support mutable and id
// feature: Add xcdrv2, mutable, optional, id, and their test cases
// ------------------------------------------------------------------

#include <ertps/xmlparser/XMLParser.h>
#include <xmlparser/XMLTreePart.h>

#include <ertps/xmlparser/XMLProfileManager.h>

#include <ertps/types/DynamicTypeBuilder.h>
#include <ertps/types/DynamicTypeBuilderPtr.h>
#include <ertps/types/DynamicTypeBuilderFactory.h>
#include <ertps/types/DynamicTypeMember.h>
#include <ertps/types/TypeDescriptor.h>
#include <ertps/types/DynamicType.h>

#include <tinyxml2.h>
#include <iostream>
#include <cstdlib>

#include <elog/FileConsumer.hpp>
#include <elog/StdoutConsumer.hpp>
#include <elog/StdoutErrConsumer.hpp>

using evbs::ertps::types::ANNOTATION_BIT_BOUND_ID;
using evbs::ertps::types::ANNOTATION_KEY_ID;
using evbs::ertps::types::ANNOTATION_POSITION_ID;
using evbs::ertps::types::DynamicTypeBuilder;
using evbs::ertps::types::DynamicTypeBuilderFactory;
using evbs::ertps::types::DynamicTypeMember;
using evbs::ertps::types::MAX_ELEMENTS_COUNT;
using evbs::ertps::types::MemberId;
using evbs::ertps::types::TK_BITSET;
using evbs::ertps::types::TK_STRUCTURE;
using evbs::ertps::xmlparser::p_dynamictypebuilder_t;
using evbs::ertps::xmlparser::XMLParser;
using evbs::ertps::xmlparser::XMLProfileManager;
using vbsutil::xmlparser::XMLP_ret;
using vbsutil::xmlparser::xmlString;

XMLP_ret XMLParser::parseXMLDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    tinyxml2::XMLElement* p_aux0 = nullptr;
    for (p_aux0 = p_root->FirstChildElement(); p_aux0 != nullptr; p_aux0 = p_aux0->NextSiblingElement()) {
        const std::string type = p_aux0->Value();
        if (type.compare(xmlString::STRUCT) == 0) {
            ret = parseXMLStructDynamicType(p_aux0);
        } else if (type.compare(xmlString::UNION) == 0) {
            ret = parseXMLUnionDynamicType(p_aux0);
        } else if (type.compare(xmlString::ENUM) == 0) {
            ret = parseXMLEnumDynamicType(p_aux0);
        } else if (type.compare(xmlString::TYPEDEF) == 0) {
            ret = parseXMLAliasDynamicType(p_aux0);
        } else if (type.compare(xmlString::BITSET) == 0) {
            ret = parseXMLBitsetDynamicType(p_aux0);
        } else if (type.compare(xmlString::BITMASK) == 0) {
            ret = parseXMLBitmaskDynamicType(p_aux0);
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing type: Type " << type << " not recognized.");
            ret = XMLP_ret::XML_ERROR;
        }

        if (ret != XMLP_ret::XML_OK) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing type " << type << ".");
            break;
        }
    }
    return ret;
}

XMLP_ret XMLParser::parseXMLTypes(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    tinyxml2::XMLElement *p_aux0 = nullptr, *p_aux1 = nullptr;
    p_aux0 = p_root->FirstChildElement(xmlString::TYPES);
    if (p_aux0 != nullptr) {
        const char* name = nullptr;
        for (p_aux1 = p_aux0->FirstChildElement(); p_aux1 != nullptr; p_aux1 = p_aux1->NextSiblingElement()) {
            name = p_aux1->Name();
            if (strcmp(name, xmlString::TYPE) == 0) {
                if (XMLP_ret::XML_OK != parseXMLDynamicType(p_aux1)) {
                    return XMLP_ret::XML_ERROR;
                }
            } else {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Invalid element found into 'types'. Name: " << name);
                return XMLP_ret::XML_ERROR;
            }
        }
    } else {  // Directly root is TYPES?
        const char* name = nullptr;
        for (p_aux0 = p_root->FirstChildElement(); p_aux0 != nullptr; p_aux0 = p_aux0->NextSiblingElement()) {
            name = p_aux0->Name();
            if (strcmp(name, xmlString::TYPE) == 0) {
                if (XMLP_ret::XML_OK != parseXMLDynamicType(p_aux0)) {
                    return XMLP_ret::XML_ERROR;
                }
            } else {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Invalid element found into 'types'. Name: " << name);
                return XMLP_ret::XML_ERROR;
            }
        }
    }
    return ret;
}

XMLP_ret XMLParser::parseXMLBitvalueDynamicType(tinyxml2::XMLElement* p_root, p_dynamictypebuilder_t p_dynamictype,
                                                uint16_t& field_position) {

    if (p_root == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing bitmask: Node not found.");
        return XMLP_ret::XML_ERROR;
    }

    const char* memberName = p_root->Attribute(xmlString::NAME);
    const char* position = p_root->Attribute(xmlString::POSITION);

    if (position != nullptr) {
        try {
            field_position = static_cast<uint16_t>(std::stoul(position));
        } catch (const std::exception&) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Error parsing bit_value position: Invalid (must be an unsigned short).");
            return XMLP_ret::XML_ERROR;
        }
    }

    if ((memberName == nullptr) || (p_dynamictype == nullptr)) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing bit_value name: Not found.");
        return XMLP_ret::XML_ERROR;
    }

    (void)p_dynamictype->add_empty_member(field_position, memberName);
    ++field_position;

    return XMLP_ret::XML_OK;
}

static p_dynamictypebuilder_t getDiscriminatorTypeBuilder(const std::string& disc, uint32_t bound = 0);

static p_dynamictypebuilder_t getDiscriminatorTypeBuilder(const std::string& disc, uint32_t bound) {
    /*
       mKind == TK_BOOLEAN || mKind == TK_BYTE || mKind == TK_INT16 || mKind == TK_INT32 ||
        mKind == TK_INT64 || mKind == TK_UINT16 || mKind == TK_UINT32 || mKind == TK_UINT64 ||
        mKind == TK_FLOAT32 || mKind == TK_FLOAT64 || mKind == TK_FLOAT128 || mKind == TK_CHAR8 ||
        mKind == TK_CHAR16 || mKind == TK_STRING8 || mKind == TK_STRING16 || mKind == TK_ENUM ||
       mKind == TK_BITMASK || mKind == TK_INT8
     */
    DynamicTypeBuilderFactory* factory = DynamicTypeBuilderFactory::get_instance();
    if (disc.compare(xmlString::BOOLEAN) == 0) {
        return factory->create_bool_builder();
    } else if ((disc.compare(xmlString::TBYTE) == 0) || (disc.compare(xmlString::OCTET) == 0) ||
               (disc.compare(xmlString::UINT8) == 0)) {
        return factory->create_byte_builder();
    } else if (disc.compare(xmlString::SHORT) == 0) {
        return factory->create_int16_builder();
    } else if (disc.compare(xmlString::LONG) == 0) {
        return factory->create_int32_builder();
    } else if (disc.compare(xmlString::LONGLONG) == 0) {
        return factory->create_int64_builder();
    } else if (disc.compare(xmlString::USHORT) == 0) {
        return factory->create_uint16_builder();
    } else if (disc.compare(xmlString::ULONG) == 0) {
        return factory->create_uint32_builder();
    } else if (disc.compare(xmlString::ULONGLONG) == 0) {
        return factory->create_uint64_builder();
    } else if (disc.compare(xmlString::FLOAT) == 0) {
        return factory->create_float32_builder();
    } else if (disc.compare(xmlString::DOUBLE) == 0) {
        return factory->create_float64_builder();
    } else if (disc.compare(xmlString::LONGDOUBLE) == 0) {
        return factory->create_float128_builder();
    } else if (disc.compare(xmlString::CHAR) == 0) {
        return factory->create_char8_builder();
    } else if (disc.compare(xmlString::WCHAR) == 0) {
        return factory->create_char16_builder();
    } else if (disc.compare(xmlString::STRING) == 0) {
        return factory->create_string_builder(bound);
    } else if (disc.compare(xmlString::WSTRING) == 0) {
        return factory->create_wstring_builder(bound);
    } else if (disc.compare(xmlString::INT8) == 0) {
        return factory->create_int8_builder();
    }

    return XMLProfileManager::getDynamicTypeByName(disc);
}

XMLP_ret XMLParser::parseXMLAliasDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;

    const char* type = p_root->Attribute(xmlString::TYPE);
    if (type != nullptr) {
        if (strcmp(type, xmlString::NON_BASIC_TYPE) == 0) {
            const char* typeNonBasicName = p_root->Attribute(xmlString::NON_BASIC_TYPE_NAME);
            if (typeNonBasicName != nullptr) {
                type = typeNonBasicName;
            } else {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing member type: Not found.");
                ret = XMLP_ret::XML_ERROR;
            }
        }
        p_dynamictypebuilder_t valueBuilder;
        if ((p_root->Attribute(xmlString::ARRAY_DIMENSIONS) != nullptr) ||
            (p_root->Attribute(xmlString::SEQ_MAXLENGTH) != nullptr) ||
            (p_root->Attribute(xmlString::MAP_MAXLENGTH) != nullptr)) {
            valueBuilder = parseXMLMemberDynamicType(p_root, nullptr, types::MEMBER_ID_INVALID);
        } else {
            uint32_t bound = 0;
            const char* boundStr = p_root->Attribute(xmlString::STR_MAXLENGTH);
            if (boundStr != nullptr) {
                bound = static_cast<uint32_t>(std::stoul(boundStr, nullptr, 0));
            }
            valueBuilder = getDiscriminatorTypeBuilder(type, bound);
        }

        if (valueBuilder != nullptr) {
            const char* name = p_root->Attribute(xmlString::NAME);
            if (name != nullptr) {
                p_dynamictypebuilder_t typeBuilder =
                    DynamicTypeBuilderFactory::get_instance()->create_alias_builder(valueBuilder, name);
                (void)XMLProfileManager::insertDynamicTypeByName(name, typeBuilder);
            } else {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing alias type: No name attribute given.");
                ret = XMLP_ret::XML_ERROR;
            }
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing alias type: Value not recognized.");
            ret = XMLP_ret::XML_ERROR;
        }
    } else {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing alias type: Type not defined.");
        ret = XMLP_ret::XML_ERROR;
    }
    return ret;
}

XMLP_ret XMLParser::parseXMLBitsetDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    p_dynamictypebuilder_t typeBuilder;
    uint32_t mId = 0;

    const char* name = p_root->Attribute(xmlString::NAME);
    if (nullptr == name) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing 'bitsetDcl' type. No name attribute given.");
        return XMLP_ret::XML_ERROR;
    }

    uint16_t position = 0;
    const char* baseType = p_root->Attribute(xmlString::BASE_TYPE);
    if (baseType != nullptr) {
        p_dynamictypebuilder_t parentType = XMLProfileManager::getDynamicTypeByName(baseType);
        if ((parentType != nullptr) && (parentType->get_kind() == TK_BITSET)) {
            typeBuilder = DynamicTypeBuilderFactory::get_instance()->create_child_struct_builder(parentType);
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Invalid baseType found into 'bitsetDcl'. Name: " << baseType);
            return XMLP_ret::XML_ERROR;
        }
        auto base_type_ptr = typeBuilder->get_type_descriptor()->get_base_type();
        std::map<MemberId, DynamicTypeMember*> members;
        base_type_ptr->get_all_members(members);
        auto last_mmeber_it = members.rbegin();
        DynamicTypeMember* last_member_ptr = last_mmeber_it->second;
        auto last_member_desc = last_member_ptr->get_descriptor();
        position = last_member_desc->annotation_get_bit_bound();
        mId = typeBuilder->get_current_member_id();
    } else {
        typeBuilder = DynamicTypeBuilderFactory::get_instance()->create_bitset_builder();
    }
    (void)typeBuilder->set_name(name);

    const char* element_name = nullptr;
    for (tinyxml2::XMLElement* p_element = p_root->FirstChildElement(); p_element != nullptr;
         p_element = p_element->NextSiblingElement()) {
        element_name = p_element->Name();
        if (strcmp(element_name, xmlString::BITFIELD) == 0) {
            p_dynamictypebuilder_t mType = parseXMLBitfieldDynamicType(p_element, typeBuilder, mId++, position);
            if (mType == nullptr) {
                return XMLP_ret::XML_ERROR;
            }
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Invalid element found into 'bitsetDcl'. Name: " << element_name);
            return XMLP_ret::XML_ERROR;
        }
    }

    (void)XMLProfileManager::insertDynamicTypeByName(name, typeBuilder);
    return ret;
}

p_dynamictypebuilder_t XMLParser::parseXMLBitfieldDynamicType(tinyxml2::XMLElement* p_root,
                                                              p_dynamictypebuilder_t p_dynamictype, MemberId mId,
                                                              uint16_t& position) {

    if (p_root == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing bitfield: Node not found.");
        return nullptr;
    }

    const char* memberType = p_root->Attribute(xmlString::TYPE);
    const char* memberName = p_root->Attribute(xmlString::NAME);
    const char* bit_bound = p_root->Attribute(xmlString::BIT_BOUND);

    if (bit_bound == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing bitfield bit_bound: Not found.");
        return nullptr;
    }

    if (memberName == nullptr) {
        memberName = "";
    }

    DynamicTypeBuilder* memberBuilder = nullptr;
    DynamicTypeBuilderFactory* factory = DynamicTypeBuilderFactory::get_instance();

    if (memberType == nullptr) {
        try {
            uint16_t size = static_cast<uint16_t>(std::stoul(bit_bound));
            if (size == 1) {
                memberType = xmlString::BOOLEAN;
            } else if (size <= 8) {
                memberType = xmlString::CHAR;
            } else if (size <= 16) {
                memberType = xmlString::USHORT;
            } else if (size <= 32) {
                memberType = xmlString::ULONG;
            } else if (size <= 64) {
                memberType = xmlString::ULONGLONG;
            } else {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Failed creating bitfield, size too big: " << bit_bound);
                return nullptr;
            }
        } catch (...) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Failed creating bitfield, invalid bit_bound (must be an unsigned short): " << bit_bound);
            return nullptr;
        }
    }

    if (strncmp(memberType, xmlString::BOOLEAN, 8) == 0) {
        memberBuilder = factory->create_bool_builder();
    } else if (strncmp(memberType, xmlString::CHAR, 5) == 0) {
        memberBuilder = factory->create_char8_builder();
    } else if (strncmp(memberType, xmlString::WCHAR, 6) == 0) {
        memberBuilder = factory->create_char16_builder();
    } else if ((strncmp(memberType, xmlString::TBYTE, 6) == 0) || (strncmp(memberType, xmlString::OCTET, 6) == 0) ||
               (strncmp(memberType, xmlString::UINT8, 6) == 0)) {
        memberBuilder = factory->create_byte_builder();
    } else if (strncmp(memberType, xmlString::INT8, 6) == 0) {
        memberBuilder = factory->create_int8_builder();
    } else if (strncmp(memberType, xmlString::SHORT, 6) == 0) {
        memberBuilder = factory->create_int16_builder();
    } else if (strncmp(memberType, xmlString::LONG, 5) == 0) {
        memberBuilder = factory->create_int32_builder();
    } else if (strncmp(memberType, xmlString::ULONG, 13) == 0) {
        memberBuilder = factory->create_uint32_builder();
    } else if (strncmp(memberType, xmlString::USHORT, 14) == 0) {
        memberBuilder = factory->create_uint16_builder();
    } else if (strncmp(memberType, xmlString::LONGLONG, 9) == 0) {
        memberBuilder = factory->create_int64_builder();
    } else if (strncmp(memberType, xmlString::ULONGLONG, 17) == 0) {
        memberBuilder = factory->create_uint64_builder();
    } else {  // Unsupported type?
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                  "Failed creating bitfield " << memberName << ": Type " << memberType << " unsupported.");
    }

    if (memberBuilder == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Failed creating " << memberType << ": " << memberName);
    }

    if (p_dynamictype != nullptr) {
        (void)p_dynamictype->add_member(mId, memberName, memberBuilder);
        (void)p_dynamictype->apply_annotation_to_member(mId, ANNOTATION_BIT_BOUND_ID, "value", bit_bound);
        // position += static_cast<uint16_t>(mId);
        (void)p_dynamictype->apply_annotation_to_member(mId, ANNOTATION_POSITION_ID, "value", std::to_string(position));
        position += static_cast<uint16_t>(std::stoul(bit_bound, nullptr, 0));
    }

    return memberBuilder;
}

XMLP_ret XMLParser::parseXMLBitmaskDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    uint16_t bit_bound = 32;
    const char* anno_bit_bound = p_root->Attribute(xmlString::BIT_BOUND);
    if (anno_bit_bound != nullptr) {
        auto input_bit_bound = std::stoul(anno_bit_bound, nullptr, 0);
        if (input_bit_bound < 1 || input_bit_bound > 64) {
            return XMLP_ret::XML_ERROR;
        }
        bit_bound = static_cast<uint16_t>(input_bit_bound);
    }

    const char* name = p_root->Attribute(xmlString::NAME);
    if (nullptr == name) {
        return XMLP_ret::XML_ERROR;
    }
    p_dynamictypebuilder_t typeBuilder = DynamicTypeBuilderFactory::get_instance()->create_bitmask_builder(bit_bound);
    (void)typeBuilder->set_name(name);
    uint16_t position = 0;

    const char* element_name = nullptr;
    for (tinyxml2::XMLElement* p_element = p_root->FirstChildElement(); p_element != nullptr;
         p_element = p_element->NextSiblingElement()) {
        element_name = p_element->Name();
        if (strcmp(element_name, xmlString::BIT_VALUE) == 0) {
            if (parseXMLBitvalueDynamicType(p_element, typeBuilder, position) != XMLP_ret::XML_OK) {
                return XMLP_ret::XML_ERROR;
            }
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Invalid element found into 'bitmaskDcl'. Name: " << element_name);
            return XMLP_ret::XML_ERROR;
        }
    }

    (void)XMLProfileManager::insertDynamicTypeByName(name, typeBuilder);
    return ret;
}

XMLP_ret XMLParser::parseXMLEnumDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    const char* enumName = p_root->Attribute(xmlString::NAME);

    if (enumName == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing 'enum' type. No name attribute given.");
        return XMLP_ret::XML_ERROR;
    }

    p_dynamictypebuilder_t typeBuilder = DynamicTypeBuilderFactory::get_instance()->create_enum_builder();
    (void)typeBuilder->set_name(enumName);
    uint32_t currValue = 0;
    for (tinyxml2::XMLElement* literal = p_root->FirstChildElement(xmlString::ENUMERATOR); literal != nullptr;
         literal = literal->NextSiblingElement(xmlString::ENUMERATOR)) {
        const char* name = literal->Attribute(xmlString::NAME);
        if (name == nullptr) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing enum type: Literals must have name.");
            return XMLP_ret::XML_ERROR;
        }

        const char* value = literal->Attribute(xmlString::VALUE);
        if (value != nullptr) {
            currValue = static_cast<uint32_t>(std::stoul(value, nullptr, 0));
        }
        (void)typeBuilder->add_empty_member(currValue++, name);
    }

    (void)XMLProfileManager::insertDynamicTypeByName(enumName, typeBuilder);
    return ret;
}

XMLP_ret XMLParser::parseXMLStructDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    const char* name = p_root->Attribute(xmlString::NAME);
    p_dynamictypebuilder_t typeBuilder;
    uint32_t mId = 0;

    const char* baseType = p_root->Attribute(xmlString::BASE_TYPE);
    if (baseType != nullptr) {
        p_dynamictypebuilder_t parentType = XMLProfileManager::getDynamicTypeByName(baseType);
        if ((parentType != nullptr) && (parentType->get_kind() == TK_STRUCTURE)) {
            typeBuilder = DynamicTypeBuilderFactory::get_instance()->create_child_struct_builder(parentType);
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Invalid baseType found into 'structDcl'. Name: " << baseType);
            return XMLP_ret::XML_ERROR;
        }
        mId = typeBuilder->get_current_member_id();
    } else {
        typeBuilder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
    }
    (void)typeBuilder->set_name(name);

    const char* memberAnnoExt = p_root->Attribute(xmlString::EXTENSIBILITY);
    if (memberAnnoExt != nullptr) {
        std::string anno_appendable = evbs::ertps::types::ANNOTATION_APPENDABLE_ID;
        std::string anno_final = evbs::ertps::types::ANNOTATION_FINAL_ID;
        std::string anno_mutable = evbs::ertps::types::ANNOTATION_MUTABLE_ID;
        if (strncmp(memberAnnoExt, anno_appendable.c_str(), anno_appendable.length() + 1) == 0) {
            (void)typeBuilder->apply_annotation(anno_appendable, "value", "true");
        } else if (strncmp(memberAnnoExt, anno_final.c_str(), anno_final.length() + 1) == 0) {
            (void)typeBuilder->apply_annotation(anno_final, "value", "true");
        } else if (strncmp(memberAnnoExt, anno_mutable.c_str(), anno_mutable.length() + 1) == 0) {
            (void)typeBuilder->apply_annotation(anno_mutable, "value", "true");
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Attribute " << xmlString::EXTENSIBILITY << " value " << memberAnnoExt << "not support.");
            return XMLP_ret::XML_ERROR;
        }
    }

    const char* element_name = nullptr;
    for (tinyxml2::XMLElement* p_element = p_root->FirstChildElement(); p_element != nullptr;
         p_element = p_element->NextSiblingElement()) {
        element_name = p_element->Name();
        if (strcmp(element_name, xmlString::MEMBER) == 0) {
            p_dynamictypebuilder_t mType = parseXMLMemberDynamicType(p_element, typeBuilder, mId++);
            if (mType == nullptr) {
                return XMLP_ret::XML_ERROR;
            }
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Invalid element found into 'structDcl'. Name: " << element_name);
            return XMLP_ret::XML_ERROR;
        }
    }

    (void)XMLProfileManager::insertDynamicTypeByName(name, typeBuilder);

    return ret;
}

XMLP_ret XMLParser::parseXMLUnionDynamicType(tinyxml2::XMLElement* p_root) {

    XMLP_ret ret = XMLP_ret::XML_OK;
    const char* name = p_root->Attribute(xmlString::NAME);
    tinyxml2::XMLElement* p_element = p_root->FirstChildElement(xmlString::DISCRIMINATOR);
    if (p_element != nullptr) {
        const char* disc = p_element->Attribute(xmlString::TYPE);
        p_dynamictypebuilder_t discriminator = getDiscriminatorTypeBuilder(disc);
        if (discriminator == nullptr) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Error parsing union discriminator: Only primitive types allowed (found type " << disc << ").");
            ret = XMLP_ret::XML_ERROR;
        } else {
            p_dynamictypebuilder_t typeBuilder =
                DynamicTypeBuilderFactory::get_instance()->create_union_builder(discriminator);
            (void)typeBuilder->set_name(name);
            uint32_t mId = 0;
            for (p_element = p_root->FirstChildElement(xmlString::CASE); p_element != nullptr;
                 p_element = p_element->NextSiblingElement(xmlString::CASE)) {
                std::string valuesStr = "";
                for (tinyxml2::XMLElement* caseValue = p_element->FirstChildElement(xmlString::CASE_DISCRIMINATOR);
                     caseValue != nullptr; caseValue = caseValue->NextSiblingElement(xmlString::CASE_DISCRIMINATOR)) {
                    const char* values = caseValue->Attribute(xmlString::VALUE);
                    if (values == nullptr) {
                        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing union case value: Not found.");
                        return XMLP_ret::XML_ERROR;
                    }

                    if (valuesStr.empty()) {
                        valuesStr = values;
                    } else {
                        valuesStr += std::string(",") + values;
                    }
                }

                tinyxml2::XMLElement* caseElement = p_element->FirstChildElement();
                while ((caseElement != nullptr) &&
                       (strncmp(caseElement->Value(), xmlString::CASE_DISCRIMINATOR, 10) == 0)) {
                    caseElement = caseElement->NextSiblingElement();
                }
                if (caseElement != nullptr) {
                    p_dynamictypebuilder_t mType =
                        parseXMLMemberDynamicType(caseElement, typeBuilder, mId++, valuesStr);
                    if (mType == nullptr) {
                        return XMLP_ret::XML_ERROR;
                    }
                } else {
                    elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing union case member: Not found.");
                    return XMLP_ret::XML_ERROR;
                }
            }

            (void)XMLProfileManager::insertDynamicTypeByName(name, typeBuilder);
        }
    } else {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing union discriminator: Not found.");
        ret = XMLP_ret::XML_ERROR;
    }

    return ret;
}

static void dimensionsToArrayBounds(const std::string& dimensions, std::vector<uint32_t>& bounds) {
    std::stringstream ss(dimensions);
    std::string item;

    bounds.clear();

    while (std::getline(ss, item, ',')) {
        bounds.push_back(static_cast<uint32_t>(std::stoul(item, nullptr, 0)));
    }
}

static bool dimensionsToLabels(const std::string& labelStr, std::vector<uint64_t>& labels) {
    std::stringstream ss(labelStr);
    std::string item;
    bool def = false;

    labels.clear();
    while (std::getline(ss, item, ',')) {
        if (item == xmlString::DEFAULT) {
            def = true;
        } else {
            labels.push_back(static_cast<uint64_t>(std::stoull(item, nullptr, 0)));
        }
    }

    return def;
}

p_dynamictypebuilder_t XMLParser::parseXMLMemberDynamicType(tinyxml2::XMLElement* p_root,
                                                            p_dynamictypebuilder_t p_dynamictype, MemberId mId) {
    return parseXMLMemberDynamicType(p_root, p_dynamictype, mId, "");
}

p_dynamictypebuilder_t XMLParser::parseXMLMemberDynamicType(tinyxml2::XMLElement* p_root,
                                                            p_dynamictypebuilder_t p_dynamictype, MemberId mId,
                                                            const std::string& values) {

    if (p_root == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing member: Node not found.");
        return nullptr;
    }

    const char* memberType = p_root->Attribute(xmlString::TYPE);
    const char* memberName = p_root->Attribute(xmlString::NAME);
    bool isArray = false;

    if ((memberName == nullptr) && (p_dynamictype != nullptr)) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing member name: Not found.");
        return nullptr;
    }

    if (memberType == nullptr) {
        elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing member type: Not found.");
        return nullptr;
    }

    const char* memberArray = p_root->Attribute(xmlString::ARRAY_DIMENSIONS);
    if (memberArray != nullptr) {
        isArray = true;
    }

    if (strcmp(memberType, xmlString::NON_BASIC_TYPE) == 0) {
        const char* memberNonBasicTypeName = p_root->Attribute(xmlString::NON_BASIC_TYPE_NAME);
        if (memberNonBasicTypeName != nullptr) {
            memberType = memberNonBasicTypeName;
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing member type: Not found.");
            return nullptr;
        }
    }

    DynamicTypeBuilder* memberBuilder = nullptr;
    DynamicTypeBuilderFactory* factory = DynamicTypeBuilderFactory::get_instance();

    const char* memberSequence = p_root->Attribute(xmlString::SEQ_MAXLENGTH);
    if (memberSequence != nullptr) {
        p_dynamictypebuilder_t contentType = getDiscriminatorTypeBuilder(memberType);
        if (contentType == nullptr) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Error parsing sequence element type: Cannot be recognized: " << memberType);
            return nullptr;
        }

        const char* lengthStr = p_root->Attribute(xmlString::SEQ_MAXLENGTH);
        uint32_t length = MAX_ELEMENTS_COUNT;
        if (lengthStr != nullptr) {
            try {
                length = static_cast<uint32_t>(std::stoul(lengthStr, nullptr, 0));
            } catch (const std::exception&) {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                          "Error parsing member sequence length in line " << p_root->GetLineNum());
                return nullptr;
            }
        }

        if (!isArray) {
            memberBuilder = factory->create_sequence_builder(contentType, length);
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_sequence_builder(contentType, length);
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (p_root->Attribute(xmlString::MAP_MAXLENGTH) != nullptr) {
        /*
            In maps allowed formats are (complex format includes the basic):
            map<map<long, long, 6>, map<long, map<long, short>,2>
            <map name="my_map" length="2">
                <key_type>
                    <map name="inner_key_map" key_type="long" value_type="long" length="6"/>
                </key_type>
                </value_type>
                    <map name="inner_value_map" key_type="long" length="2">
                        </value_type>
                            <map name="inner_value_value_map" key_type="long" value_type="short"/>
                        </value_type>
                    </map>
                </value_type>
            </map>
            In this example, inner maps names are ignored and can be omited.
         */
        // Parse key

        // const char* keyType = p_root->Attribute(KEY);
        p_dynamictypebuilder_t keyTypeBuilder = nullptr;
        const char* memberMapKeyType = p_root->Attribute(xmlString::MAP_KEY_TYPE);
        if (memberMapKeyType != nullptr) {
            keyTypeBuilder = getDiscriminatorTypeBuilder(memberMapKeyType);
            if (keyTypeBuilder == nullptr) {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                          "Error parsing map's key element type: Cannot be recognized.");
                return nullptr;
            }
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing key_type element: Not found.");
            return nullptr;
        }

        // Parse value
        p_dynamictypebuilder_t valueTypeBuilder;
        if (memberType != nullptr) {
            valueTypeBuilder = getDiscriminatorTypeBuilder(memberType);
            if (valueTypeBuilder == nullptr) {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                          "Error parsing map's value element type: Cannot be recognized.");
                return nullptr;
            }
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR, "Error parsing value_value element: Not found.");
            return nullptr;
        }

        const char* lengthStr = p_root->Attribute(xmlString::MAP_MAXLENGTH);
        uint32_t length = MAX_ELEMENTS_COUNT;
        if (lengthStr != nullptr) {
            try {
                length = static_cast<uint32_t>(std::stoul(lengthStr, nullptr, 0));
            } catch (const std::exception&) {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                          "Error parsing map member sequence length in line " << p_root->GetLineNum()) return nullptr;
            }
        }

        if (!isArray) {
            memberBuilder = factory->create_map_builder(keyTypeBuilder, valueTypeBuilder, length);
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_map_builder(keyTypeBuilder, valueTypeBuilder, length);
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::BOOLEAN, 8) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_bool_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_bool_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::CHAR, 5) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_char8_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_char8_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if ((strncmp(memberType, xmlString::INT8, 5) == 0)) {
        if (!isArray) {
            memberBuilder = factory->create_int8_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_int8_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::WCHAR, 6) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_char16_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_char16_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if ((strncmp(memberType, xmlString::TBYTE, 6) == 0) || (strncmp(memberType, xmlString::OCTET, 6) == 0) ||
               (strncmp(memberType, xmlString::UINT8, 6) == 0)) {
        if (!isArray) {
            memberBuilder = factory->create_byte_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_byte_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::SHORT, 6) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_int16_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_int16_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::LONG, 5) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_int32_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_int32_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::ULONG, 13) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_uint32_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_uint32_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::USHORT, 14) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_uint16_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_uint16_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::LONGLONG, 9) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_int64_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_int64_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::ULONGLONG, 17) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_uint64_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_uint64_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::FLOAT, 6) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_float32_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_float32_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::DOUBLE, 7) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_float64_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_float64_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::LONGDOUBLE, 11) == 0) {
        if (!isArray) {
            memberBuilder = factory->create_float128_builder();
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_float128_builder();
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::STRING, 7) == 0) {
        uint32_t bound = 0;
        const char* boundStr = p_root->Attribute(xmlString::STR_MAXLENGTH);
        if (boundStr != nullptr) {
            bound = static_cast<uint32_t>(std::stoul(boundStr, nullptr, 0));
        }
        if (!isArray) {
            memberBuilder = factory->create_string_builder(bound);
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_string_builder(bound);
            std::vector<uint32_t> boundsArray;
            dimensionsToArrayBounds(memberArray, boundsArray);
            memberBuilder = factory->create_array_builder(innerBuilder, boundsArray);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else if (strncmp(memberType, xmlString::WSTRING, 8) == 0) {
        uint32_t bound = 0;
        const char* boundStr = p_root->Attribute(xmlString::STR_MAXLENGTH);
        if (boundStr != nullptr) {
            bound = static_cast<uint32_t>(std::stoul(boundStr, nullptr, 0));
        }
        if (!isArray) {
            memberBuilder = factory->create_wstring_builder(bound);
        } else {
            DynamicTypeBuilder* innerBuilder = factory->create_wstring_builder(bound);
            std::vector<uint32_t> boundsArray;
            dimensionsToArrayBounds(memberArray, boundsArray);
            memberBuilder = factory->create_array_builder(innerBuilder, boundsArray);
            // factory->DeleteBuilder(innerBuilder);
        }
    } else {  // Complex type?
        p_dynamictypebuilder_t typePtr = XMLProfileManager::getDynamicTypeByName(memberType);
        if (!isArray) {
            memberBuilder = typePtr;
        } else {
            DynamicTypeBuilder* innerBuilder = typePtr;
            std::vector<uint32_t> bounds;
            dimensionsToArrayBounds(memberArray, bounds);
            memberBuilder = factory->create_array_builder(innerBuilder, bounds);
            // Don't delete innerBuilder, it will be freed on destructors.
        }
    }

    if (memberBuilder == nullptr) {
        if (!isArray) {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Failed creating " << memberType << ": " << (memberName ? memberName : ""));
        } else {
            elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                      "Failed creating " << memberType << " array: " << (memberName ? memberName : ""));
        }
        return nullptr;
    }

    std::string default_value("");
    const char* memberDefault = p_root->Attribute(xmlString::DEFAULT);
    if (memberDefault != nullptr) {
        default_value = memberDefault;
    }

    if (p_dynamictype != nullptr) {
        if (!values.empty()) {
            std::vector<uint64_t> labels;
            bool defaultLabel = dimensionsToLabels(values, labels);
            if (p_dynamictype->add_member(mId, memberName, memberBuilder, default_value, labels, defaultLabel) !=
                RetCode_t::RETCODE_OK) {
                return nullptr;
            }
        } else {
            if (p_dynamictype->add_member(mId, memberName, memberBuilder, default_value) != RetCode_t::RETCODE_OK) {
                return nullptr;
            }
        }

        const char* memberTopicEpkey = p_root->Attribute(xmlString::EPKEY);
        if (memberTopicEpkey != nullptr) {
            if (strncmp(memberTopicEpkey, "true", 5) == 0) {
                if (p_dynamictype->apply_annotation_to_member(mId, evbs::ertps::types::ANNOTATION_EPKEY_ID, "value",
                                                              "true") != RetCode_t::RETCODE_OK) {
                    return nullptr;
                }
            }
        }

        const char* memberTopicKey = p_root->Attribute(xmlString::KEY);
        if (memberTopicKey != nullptr) {
            if (strncmp(memberTopicKey, "true", 5) == 0) {
                if (p_dynamictype->apply_annotation_to_member(mId, evbs::ertps::types::ANNOTATION_KEY_ID, "value",
                                                              "true") != RetCode_t::RETCODE_OK) {
                    return nullptr;
                }
            }
        }

        const char* memberOptional = p_root->Attribute(xmlString::ANNO_OPTIONAL);
        if (memberOptional != nullptr) {
            if (strncmp(memberOptional, "true", 5) == 0) {
                if (p_dynamictype->apply_annotation_to_member(mId, evbs::ertps::types::ANNOTATION_OPTIONAL_ID, "value",
                                                              "true") != RetCode_t::RETCODE_OK) {
                    return nullptr;
                }
            }
        }

        const char* memberAnnoId = p_root->Attribute(xmlString::ID);
        if (memberAnnoId != nullptr) {
            uint16_t id = 0;
            try {
                id = static_cast<uint16_t>(std::stoul(memberAnnoId, nullptr, 0));
            } catch (...) {
                elogError(XMLPARSER, RetCode_t::RETCODE_ERROR,
                          "member '" << memberName << "' id '" << memberAnnoId << "' invalid.");
                return nullptr;
            }
            if (p_dynamictype->apply_annotation_to_member(mId, evbs::ertps::types::ANNOTATION_ID, "value",
                                                          std::to_string(id)) != RetCode_t::RETCODE_OK) {
                return nullptr;
            }
            if (p_dynamictype->apply_cdr_id_to_member(mId, id) != RetCode_t::RETCODE_OK) {
                return nullptr;
            }
        }

        const char* memberDefault = p_root->Attribute(xmlString::DEFAULT);
        if (memberDefault != nullptr) {
            if (p_dynamictype->apply_annotation_to_member(mId, evbs::ertps::types::ANNOTATION_DEFAULT_ID, "value",
                                                          memberDefault) != RetCode_t::RETCODE_OK) {
                return nullptr;
            }
        }
    }

    return memberBuilder;
}

XMLP_ret XMLParser::parseXMLDynamicTypes(tinyxml2::XMLElement& types) {
    XMLP_ret ret = XMLP_ret::XML_OK;
    ret = parseDynamicTypes(&types);
    return ret;
}

XMLP_ret XMLParser::parseDynamicTypes(tinyxml2::XMLElement* p_root) {
    return parseXMLTypes(p_root);
}

XMLP_ret XMLParser::loadXMLDynamicTypes(tinyxml2::XMLElement& xmlDoc) {
    return parseXMLDynamicTypes(xmlDoc);
}
